<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Nib-Loading HOWTO</title>
    <link rel="stylesheet" type="text/css" href="HOWTO_files/stylesheets/styles.css" />
  </head>

  <body>

    <div class="title">
      <h1>Nib-Loading HOWTO</h1>
    </div>

    <div class="body-text">
      <p>This HOWTO shows how you can load <strong>nibfiles</strong>
        into a running copy of Clozure CL by evaluating Lisp forms. You
        might want to load nibfiles this way to test user-interface
        elements that you are working on for an application project, or
        to enable an application to dynamically load optional
        user-interface elements.</p>

    </div>

    <div class="section-head">
      <h2>Nibfiles</h2>
    </div>

    <div class="body-text">
      <p>A large part of developing Cocoa applications is creating
        user-interface elements using the Cocoa frameworks. Although
        it's perfectly possible to create any user-interface element
        just by making method calls against the frameworks, the more
        standard way to design a user interface is to use Apple's
        InterfaceBuilder application to
        create <strong>nibfiles</strong>&mdash;files of archived
        Objective-C objects that implement the user-interface
        elements.</p>
      
      <p>InterfaceBuilder is an application that ships with Apple's
        Developer Tools. The Developer Tools are an optional install
        that comes with Mac OS X. Before you can use this HOWTO, you'll
        need to make sure that Apple's Developer Tools are installed on
        your system. Members of Apple's developer programs may download
        the tools for free from
        Apple's <href="http://developer.apple.com">developer
          website</href>, but normally there is no need. You can simply
        use the optional Developer Tools installer on the Mac OS X
        system disks to install the tools.</p>
    </div>

    <div class="section-head">
      <h2>Using Nibfiles</h2> 
    </div>

    <div class="body-text">
      <p>Using InterfaceBuilder, you can quickly and easily create
        windows, dialog boxes, text fields, buttons, and other
        user-interface elements. The elements you create with
        InterfaceBuilder have the standard appearance and behavior
        specified by Apple's Human Interface Guidelines.</p>

      <p>InterfaceBuilder saves descriptions of these objects
        in <strong>nibfiles</strong>. These files contain archived
        representations of Objective-C classes and objects. When you
        launch an application and it loads a nibfile, the Cocoa runtime
        creates these Objective-C objects in memory, complete with any
        instance-variable references to other objects that might have
        been saved in the nibfile. In short, a nibfile is an archived
        collection of user-interface objects that Cocoa can quickly and
        easily revive in memory.</p>

      <p>The normal way that Objective-C programmers use nibfiles is
        by storing them in an application bundle. The application's
        Info.plist file (also stored in the bundle) specifies which
        nibfile is the application's main nibfile, and that file is
        loaded automatically when the application starts up. The
        application can dynamically load other nibfiles from the bundle
        by making method calls.</p>

      <p>Lisp applications written with Clozure CL can also use
        nibfiles in this same fashion (see the "currency-converter"
        HOWTO in the "cocoa" examples folder), but Lisp programmers are
        accustomed to highly interactive development, and might want to
        simply load an arbitrary nibfile into a running Clozure CL
        session. Fortunately, this is easy to do.</p>
    </div>

    <div class="section-head">
      <h2>How To Load a Nibfile</h2> 
    </div>

    <div class="body-text">
      <p>Let's start by loading a very simple nibfile from the Clozure
        CL Listener window. Start by launching the Clozure CL
        application.</p>

      <p>In the same directory as this HOWTO file, you'll find a
        nibfile named "hello.nib". This is an extremely simple nibfile
        that creates a single Cocoa window with a greeting in it. We'll
        use forms typed into the Listener window to load it.</p>

      <p>We're going to call the Objective-C class
        method <code>loadNibFile:externalNameTable:withZone:</code> to
        load the nibfile into memory, creating the window that is
        described in the file. First, though, we need to set up some
        data structures that we'll pass to this method.</p>

      <p>The arguments
        to <code>loadNibFile:externalNameTable:withZone:</code> are a
        pathname, a dictionary object, and a memory zone. As with every
        Objective-C method call, we also pass the object that receives
        the message, which in this case is the class NSBundle.</p>

      <p>The pathname is just a reference to the nibfile we want to
        load. The dictionary holds references to objects. In this
        first simple example, we'll use it only to identify the
        nibfile's owner, which in this case is the application
        itself. The zone is a reference to the area of memory where
        the nibfile objects will be allocated.</p>

      <p>Don't worry if none of this makes sense to you; the code to
        create these objects is simple and straightforward, and should
        help clarify what's going on.</p>

      <div class="section-head">
        <h3>1. Get the Zone</h3> 
      </div>

      <p>First, we'll get a memory zone. We'll tell Cocoa to allocate
        the nibfile objects in the same zone that the application
        uses, so getting a zone is a simple matter of asking the
        application for the one it's using.</p>

      <p>Before we can ask the application anything, we need a
        reference to it.  When the Clozure CL application starts up,
        it stores a reference to the Cocoa application object into
        the special variable *NSApp*.</p>

      <p>Start by changing to the CCL package; most of the utility
        functions we'll use are defined in that package:</p>

      <pre>
        ? (in-package :ccl)
        #&lt;Package "CCL"&gt;
      </pre>

      <p>We have a reference to the running Clozure CL application
        object in the special variable *NSApp*.  We can ask it for its
        zone, where it allocates objects in memory:</p>

      <pre>
        ? (setf *my-zone* (#/zone *NSApp*))
        #&lt;A Foreign Pointer #x8B000&gt;
      </pre>

      <p>Now we have a reference to the application's zone, which is
        one of the parameters we need to pass
        to <code>loadNibFile:externalNameTable:withZone:</code>.</p>

      <div class="section-head">
        <h3>2. Make a Dictionary</h3> 
      </div>

      <p>The dictionary argument
        to <code>loadNibFile:externalNameTable:withZone:</code> is
        used for two purposes: to identify the nibfile's owner, and
        to collect toplevel objects.</p>

      <p>The nibfile's owner becomes the owner of all the toplevel
        objects created when the nibfile is loaded, objects such as
        windows, buttons, and so on. A nibfile's owner manages the
        objects created when the nibfile is loaded, and provides a
        way for your code to get references to those objects. You
        supply an owner object in the dictionary, under the
        key <code>"NSNibOwner"</code>.</p>

      <p>The toplevel objects are objects, such as windows, that are
        created when the nibfile is loaded. To collect these, you
        can pass an <code>NSMutableArray</code> object under the
        key <code>NSNibTopLevelObjects</code>.</p>

      <p>For this first example, we'll pass an owner object (the
        application object), but we don't need to collect toplevel
        objects, so we'll omit
        the <code>NSNibTopLevelObjects</code> key.</p>

      <pre>
        ? (setf *my-dict* 
        (#/dictionaryWithObject:forKey: ns:ns-mutable-dictionary
        *my-app* 
        #@"NSNibOwner"))
        #&lt;NS-MUTABLE-DICTIONARY {
        NSNibOwner = &lt;LispApplication: 0x1b8e10&gt;;
        } (#x137F3DD0)&gt;
        
      </pre>

      <div class="section-head">
        <h3>3. Load the Nibfile</h3> 
      </div>

      <p>Now that we have the zone and the dictionary we need, we
        can load the nibfile. We just need to create an NSString with
        the proper pathname first:</p>

      <pre>
        ? (setf *nib-path* 
        (%make-nsstring 
        (namestring "/usr/local/openmcl/ccl/examples/cocoa/nib-loading/hello.nib")))
        #&lt;NS-MUTABLE-STRING "/usr/local/openmcl/ccl/examples/cocoa/nib-loading/hello.nib" (#x13902C10)&gt;
      </pre>

      <p>Now we can actually load the nibfile, passing the method
        the objects we've created:</p>

      <pre>
        ? (#/loadNibFile:externalNameTable:withZone: 
        ns:ns-bundle
        *nib-path*
        *my-dict*
        *my-zone*)
        T
      </pre>

      <p>The window defined in the "hello.nib" file should appear
        on the
        screen. The <code>loadNibFile:externalNameTable:withZone:</code>
        method returns <code>T</code> to indicate it loaded the
        nibfile successfully; if it had failed, it would have
        returned <code>NIL</code>.</p>

      <p>At this point we no longer need the pathname and
        dictionary objects.  The *nib-path* we must release:</p>

      <pre>
        ? (setf *nib-path* (#/release *nib-path*))
        NIL
      </pre>

      <p>The *my-dict* instance was not created with #/alloc (or with
      MAKE-INSTANCE), so it is already autoreleased, and we don't need
      to release it again.

      <div class="section-head">
        <h2>Making a Nib-loading Function</h2> 
      </div>

      <p>Loading a nibfile seems like something we might want to do
        repeatedly, and so it makes sense to make it as easy as possible
        to do. Let's make a single function we can call to load a nib as
        needed.</p>

      <p>The nib-loading function can take the file to be loaded as a
      parameter, and then perform the sequence of steps covered in the
      previous section. If we just literally do that, the result will
      look something like this:</p>

      <pre>
(defun load-nibfile (nib-path)
  (let* ((app-zone (#/zone *NSApp*))
         (nib-name (%make-nsstring (namestring nib-path)))
         (dict (#/dictionaryWithObject:forKey: 
                 ns-mutable-dictionary app #@"NSNibOwner")))
    (#/loadNibFile:externalNameTable:withZone: ns:ns-bundle
                                               nib-name
                                               dict
                                               app-zone
                                             )))
      </pre>

      <p>The trouble with this function is that it leaks a string
      every time we call it. We need to release the
      <code>nib-name</code> before returning. So how about this
      version instead?</p>

      <pre>
(defun load-nibfile (nib-path)
  (let* ((app-zone (#/zone *NSApp*))
         (nib-name (%make-nsstring (namestring nib-path)))
         (dict (#/dictionaryWithObject:forKey: 
                ns-mutable-dictionary app #@"NSNibOwner"))
         (result (#/loadNibFile:externalNameTable:withZone: ns:ns-bundle
                                                            nib-name
                                                            dict
                                                            app-zone)))
    (#/release nib-name)
    result))
      </pre>

      <p>This version solves the leaking problem by binding the result
      of the load call to <code>result</code>, then releasing the
      <code>nib-name</code> before returning the result of the
      load.</p>

      <p>There's just one more problem: what if we want to use the
      dictionary to collect the nibfile's toplevel objects, so that we
      can get access to them from our code? We'll need another version
      of our function.</p>

      <p>In order to collect toplevel objects, we'll want to pass an
      NSMutableArray object in the dictionary, stored under the key
      <code>NSNibTopLevelObjects</code>. So we first need to create such an
      array object in the <code>let</code> form:</p>

      <pre>
(let* (...
       (objects-array (#/arrayWithCapacity: ns:ns-mutable-array 16))
       ...)
  ...)
      </pre>

      <p>Now that we have the array in which to store the nibfile's
      toplevel objects, we need to change the code that creates the
      dictionary, so that it contains not only the owner object, but
      also the array we just created:</p>

      <pre>
  (let* (...
         (dict (#/dictionaryWithObjectsAndKeys: ns:ns-mutable-dictionary
                    app #@"NSNibOwner"
                    objects-array #&amp;NSNibTopLevelObjects
                    +null-ptr+))
         ...)
    ...)
  
      </pre>

      <p>We now want to collect the objects in it. We'll do that by
      making a local variable to store them, then iterating over the
      array object to get them all.  (Normally, when we want to keep
      an object from an array, we have to retain it.  Top-level nib
      objects are a special case: they are created by the nib loading
      process with a retain count of 1, and we are responsible for releasing
      them when we're through with them.)</p>

      <pre>
  (let* (...
         (toplevel-objects (list))
         ...)
    (dotimes (i (#/count objects-array))
      (setf toplevel-objects 
            (cons (#/objectAtIndex: objects-array i)
                  toplevel-objects)))
    ...)
      </pre>

      <p>After collecting the objects, we can release the array, then
      return the list of objects. It's still possible we might want
      to know whether the load succeeded, so we
      use <code>values</code> to return both the toplevel objects and
      the success or failure of the load.</p>

      <p>The final version of the nib-loading code looks like
      this:</p>

      <pre>
(defun load-nibfile (nib-path)
  (let* ((app-zone (#/zone *NSApp*))
         (nib-name (%make-nsstring (namestring nib-path)))
         (objects-array (#/arrayWithCapacity: ns:ns-mutable-array 16))
         (dict (#/dictionaryWithObjectsAndKeys: ns:ns-mutable-dictionary
                    *NSApp* #@"NSNibOwner"
                    objects-array #&NSNibTopLevelObjects
		    +null-ptr+))
         (toplevel-objects (list))
         (result (#/loadNibFile:externalNameTable:withZone: ns:ns-bundle
                                                            nib-name
                                                            dict
                                                            app-zone)))
    (dotimes (i (#/count objects-array))
      (setf toplevel-objects 
            (cons (#/objectAtIndex: objects-array i)
                  toplevel-objects)))
    (#/release nib-name)
    (values toplevel-objects result)))
      </pre>

      <p>Now we can call this function with some suitable nibfile,
      such as simple "hello.nib" that comes with this HOWTO:</p>

      <pre>
? (ccl::load-nibfile "hello.nib")
(#&lt;LISP-APPLICATION &lt;LispApplication: 0x1b8da0&gt; (#x1B8DA0)&gt;
 #&lt;NS-WINDOW &lt;NSWindow: 0x171344d0&gt; (#x171344D0)&gt;)
T

      </pre>

      <p>The "Hello!" window appears on the screen, and two values are
      returned. The first value is the list of toplevel objects that
      were loaded. The second value, <code>T</code> indicates that the
      nibfile was loaded successfully.</p>

      <div class="section-head">
        <h2>What About Unloading Nibfiles?</h2> 
      </div>
      
      <p>Cocoa provides no general nibfile-unloading API. Instead, if
      you want to unload a nib, the accepted approach is to close all
      the windows associated with a nibfile and release all the
      toplevel objects. This is one reason that you might want to use
      the <code>"NSNibTopLevelObjects"</code> key with the dictionary
      object that you pass
      to <code>loadNibFile:externalNameTable:withZone:</code>&mdash;to
      obtain a collection of toplevel objects that you release when
      the nibfile is no longer needed.</p>

      <p>In document-based Cocoa applications, the main nibfile is
      usually owned by the application object, and is never unloaded
      while the application runs. Auxliliary nibfiles are normally
      owned by controller objects, usually instances of
      <code>NSWindowController</code> subclasses. When you
      use <code>NSWindowController</code> objects to load nibfiles,
      they take responsibility for loading and unloading nibfile
      objects.</p>

      <p>When you're experimenting interactively with nibfile loading,
      you may not start out by
      creating <code>NSWindowController</code> objects to load
      nibfiles, and so you may need to do more of the object
      management yourself. On the one hand, loading nibfiles by hand
      is not likely to be the source of major application problems. On
      the other hand, if you experiment with nib-loading for a long
      time in an interactive session, it's possible that you'll end up
      with numerous discarded objects cluttering memory, along with
      various references to live and possibly released objects. Keep
      this in mind when using the Listener to explore Cocoa. You can
      always restore your Lisp system to a clean state by restarting
      it, but of course you then lose whatever state you have built up
      in your explorations. It's often a good idea to work from a text
      file rather than directly in the Listener, so that you have a
      record of the experimenting you've done. That way, if you need
      to start fresh (or if you accidentally cause the application to
      crash), you don't lose all the information you gained.</p>

    </div>

  </body>
</html>

